package org.ielse.frame.view;

import java.util.Stack;

import org.ielse.frame.ViewCallback;
import org.ielse.frame.util.LogUtil;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;

import com.nineoldandroids.view.ViewHelper;

public class PagerLayout extends FrameLayout {
	private int mWidth;

	private FrameLayout frontContainer;
	private FrameLayout contentContainer;
	private final Stack<FrameLayout> pageContainers = new Stack<FrameLayout>();
	private FrameLayout layCurrPageWrap;
	private FrameLayout layLastPageWrap;

	private float downX;
	private float downCurrPageTransX, downLastPageTransX;

	private float interceptDownX;
	private float touchSclop;
	private float backTouchWidth;

	private boolean anim;
	private boolean touchMove;
	private boolean slidingPage;
	private float tmpMoveProgress; // 0 ~ 1

	private ViewCallback viewCallback;

	public PagerLayout(Context context) {
		this(context, null);
	}

	public PagerLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		viewCallback = (ViewCallback) context;
		setLayerType(LAYER_TYPE_SOFTWARE, null);
		touchSclop = ViewConfiguration.get(context).getScaledTouchSlop();

		contentContainer = new FrameLayout(context);
		addView(contentContainer);
		frontContainer = new FrameLayout(context);
		addView(frontContainer);
	}

	public void addViewToFront(View view) {
		frontContainer.addView(view);
	}

	public void pushNewContent(View view) {
		pushNewContent(view, true);
	}

	public void pushNewContent(View view, boolean anim) {
		final FrameLayout layContentWrap = new FrameLayout(getContext());
		layContentWrap.addView(view);

		View lastWrap = null;
		if (!pageContainers.isEmpty()) {
			lastWrap = pageContainers.peek();
		}

		final View mLastContentWrap = lastWrap;
		if (anim) {
			ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(300);
			animator.addUpdateListener(new AnimatorUpdateListener() {
				@Override public void onAnimationUpdate(ValueAnimator animation) {
					float value = (Float) animation.getAnimatedValue();
					if (mLastContentWrap != null) {
						ViewHelper.setTranslationX(mLastContentWrap, -(mWidth / 2) * value);
					}
					ViewHelper.setTranslationX(layContentWrap, getWidth() * (1 - value));
				}
			});
			animator.addListener(new AnimatorListenerAdapter() {
				@Override public void onAnimationEnd(Animator animation) {
					if (mLastContentWrap != null) {
						mLastContentWrap.setVisibility(View.GONE);
					}
				}
			});
			animator.start();
		}
		else {
			if (mLastContentWrap != null) {
				mLastContentWrap.setVisibility(View.GONE);
				final float value = 1;
				ViewHelper.setTranslationX(mLastContentWrap, -(mWidth / 2) * value);
			}
		}

		pageContainers.add(layContentWrap);
		contentContainer.addView(layContentWrap);
	}

	public View popToLastContent() {
		return popToLastContent(true);
	}

	public View popToLastContent(boolean anim) {
		if (pageContainers.size() < 2) {
			// Not enough item
			return null;
		}
		final FrameLayout currContentWrap = pageContainers.pop();
		final float currContentWrapTranslateX = currContentWrap.getTranslationX();
		final FrameLayout lastContentWrap = pageContainers.peek();
		final float lastContentWrapTranslateX = lastContentWrap.getTranslationX();

		if (anim) {
			ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(300);
			animator.addUpdateListener(new AnimatorUpdateListener() {
				@Override public void onAnimationUpdate(ValueAnimator animation) {
					float value = (Float) animation.getAnimatedValue();
					ViewHelper.setTranslationX(currContentWrap, currContentWrapTranslateX + (mWidth - currContentWrapTranslateX) * value);
					ViewHelper.setTranslationX(lastContentWrap, lastContentWrapTranslateX * (1 - value));
				}
			});
			animator.addListener(new AnimatorListenerAdapter() {
				@Override public void onAnimationEnd(Animator animation) {
					removeView(currContentWrap);
				}
			});
			animator.start();
			lastContentWrap.setVisibility(View.VISIBLE);
		}
		else {
			removeView(currContentWrap);
			lastContentWrap.setVisibility(View.VISIBLE);
		}

		viewCallback.onViewCallback(ViewCallback.ACTION_LAY_PAGE_UP_TO_LAST, tmpMoveProgress);
		return lastContentWrap;
	}

	public void returnToCurrentContent(float progress) {
		if (pageContainers.size() < 2) {
			// Not enough item
			return;
		}
		ValueAnimator animator = ValueAnimator.ofFloat(progress, 0).setDuration(300);
		animator.addUpdateListener(new AnimatorUpdateListener() {
			@Override public void onAnimationUpdate(ValueAnimator animation) {
				final float progress = (Float) animation.getAnimatedValue();
				setToLastAnimationProgress(progress);
			}
		});
		animator.addListener(new AnimatorListenerAdapter() {
			@Override public void onAnimationEnd(Animator animation) {
				pageContainers.get(pageContainers.size() - 2).setVisibility(View.GONE);
			}
		});
		animator.start();
		viewCallback.onViewCallback(ViewCallback.ACTION_LAY_PAGE_UP_TO_CURRENT, tmpMoveProgress);
	}

	public void setToLastAnimationProgress(float progress) {
		if (pageContainers.size() < 2) {
			// Not enough item
			return;
		}
		final FrameLayout curr = pageContainers.get(pageContainers.size() - 1);
		final FrameLayout last = pageContainers.get(pageContainers.size() - 2);
		last.setVisibility(View.VISIBLE);

		float value = progress;
		ViewHelper.setTranslationX(curr, mWidth * value);
		ViewHelper.setTranslationX(last, -mWidth / 2 * (1 - value));
	}

	@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		LogUtil.i("PagerLayout onSizeChanged() w :" + w);
		mWidth = w;
		backTouchWidth = mWidth / 5;
	}

	@Override public boolean onInterceptTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			interceptDownX = ev.getRawX();
			break;
		case MotionEvent.ACTION_MOVE:
			if (!isOnlyOnePageOrEmpty() && interceptDownX < backTouchWidth && Math.abs(ev.getRawX() - interceptDownX) > touchSclop) {
				return true;
			}
			break;
		}
		return super.onInterceptTouchEvent(ev);
	}

	@Override public boolean onTouchEvent(MotionEvent event) {
		if (!anim) {
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				return true;
			case MotionEvent.ACTION_MOVE:
				if (!touchMove && !isOnlyOnePageOrEmpty()) {
					touchMove = true;
					downX = event.getRawX();
					layCurrPageWrap = pageContainers.get(pageContainers.size() - 1);
					layLastPageWrap = pageContainers.get(pageContainers.size() - 2);
					layLastPageWrap.setVisibility(View.VISIBLE);
					downCurrPageTransX = layCurrPageWrap.getTranslationX();
					downLastPageTransX = layLastPageWrap.getTranslationX();
				}
				if (!slidingPage) {
					if (!isOnlyOnePageOrEmpty() && downX < backTouchWidth && Math.abs(event.getRawX() - downX) > touchSclop) {
						slidingPage = true;
					}
				}
				else {
					float currPageResult = downCurrPageTransX + event.getRawX() - downX;
					if (currPageResult < 0) {
						currPageResult = 0;
					}
					layCurrPageWrap.setTranslationX(currPageResult);
					float lastResult = downLastPageTransX + (event.getRawX() - downX) / 2;
					if (lastResult > 0) {
						lastResult = 0;
					}
					layLastPageWrap.setTranslationX(lastResult);
					tmpMoveProgress = currPageResult / mWidth;
					viewCallback.onViewCallback(ViewCallback.ACTION_LAY_PAGE_MOVE, tmpMoveProgress);
				}
				break;
			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_UP:
				if (!isOnlyOnePageOrEmpty() && slidingPage) {
					if (event.getRawX() - downX > backTouchWidth) {
						popToLastContent();

						tmpMoveProgress = 1;
					}
					else {
						returnToCurrentContent(tmpMoveProgress);

						tmpMoveProgress = 0;
					}
				}
				touchMove = false;
				slidingPage = false;
				break;
			}
		}
		return super.onTouchEvent(event);
	}

	public boolean isOnlyOnePageOrEmpty() {
		return pageContainers.size() <= 1;
	}
}
